#![allow(unused_variables)]
#![allow(dead_code)]

use super::conf::train_conf::TrainConf;
use super::conf::{read_train_conf_from_toml, write_train_conf_to_toml};
use super::Project;
use crate::prequire::imagemagick::ConvertCmd;
use crate::prequire::tesseract::MakeBoxCmd;
use crate::prequire::BinaryCommand;
use clap::Subcommand;
use std::io::Error;
use std::path::{Path, PathBuf};

#[derive(Debug, Subcommand)]
pub enum ProjectTrainCommands {
  Init {
    #[arg(short, long)]
    lang: String,

    #[arg(short, long)]
    font: String,
  },
  ConvertSample {
    #[arg(short, long)]
    lang: String,

    #[arg(short, long)]
    font: String,

    sample_dir: String,
  },
}

pub fn serve_commands(proj: &Project, command: &ProjectTrainCommands) {
  match command {
    ProjectTrainCommands::Init { lang, font } => {
      let train_conf = TrainConf::new(lang.to_string(), font.to_string());
      let train_dir = PathBuf::from(&proj.proj_conf.app.train_dir);
      let train_dir = iif!(train_dir.starts_with("/"), train_dir, {
        let mut p = PathBuf::from(&proj.location);
        p.push(&proj.proj_conf.app.train_dir);
        p
      });

      write_train_conf_to_toml(&train_dir, &train_conf, false).expect("write train conf failed");
    }
    ProjectTrainCommands::ConvertSample {
      lang,
      font,
      sample_dir,
    } => {
      let train_base_dir = proj.resolve_train_base_dir();
      let train_conf = read_train_conf_from_toml(&train_base_dir, lang, font)
        .expect("fail read train config file from!");

      // let output = format!("{}/{}.tiff", TrainConf::resolve_dir(&train_base_dir, lang, font).display(), train_conf.num);
      let glob_pattern = format!(
        "{}/{}/*.png",
        iif!(
          sample_dir.starts_with("/"),
          "",
          proj.location.to_str().unwrap()
        ),
        &sample_dir
      );

      let mut trainer = TesseractTrainer::from_toml(train_conf, train_base_dir);
      trainer.merge_to_tiff(&[glob_pattern]);
      trainer.make_box();
      trainer.sync_to_toml();
      // write_train_conf_to_toml(&train_base_dir, &train_conf, true).expect("update train conf failed");
    }
  }
}

pub trait OcrTrainer {
  type Output;
  fn train(&self) -> Self::Output;
}

pub struct TesseractTrainer {
  dir: PathBuf,
  conf: Option<TrainConf>,
}

impl TesseractTrainer {
  pub fn new() -> Self {
    TesseractTrainer {
      dir: PathBuf::new(),
      conf: None,
    }
  }

  pub fn from_toml<P>(conf: TrainConf, train_base_dir: P) -> Self
  where
    P: AsRef<Path>,
  {
    let dir = TrainConf::resolve_dir(train_base_dir, &conf.lang, &conf.font);
    TesseractTrainer {
      dir,
      conf: Some(conf),
    }
  }

  fn gen_train_file_name_with_suffix(
    &self,
    lang: &str,
    font: &str,
    num: usize,
    suffix: &str,
  ) -> String {
    format!(
      "{}/{}.{}.exp{}{}",
      self.dir.display(),
      lang,
      font,
      num,
      suffix
    )
  }

  fn gen_train_tiff_file_name(&self) -> String {
    let conf = self.conf.as_ref().unwrap();
    self.gen_train_file_name_with_suffix(&conf.lang, &conf.font, conf.num, ".tiff")
  }
  fn gen_train_file_name(&self) -> String {
    let conf = self.conf.as_ref().unwrap();
    self.gen_train_file_name_with_suffix(&conf.lang, &conf.font, conf.num, "")
  }

  fn has_tiff(&self) -> bool {
    let tiff_path = self.gen_train_tiff_file_name();
    Path::new(&tiff_path).exists()
  }
  pub fn merge_to_tiff(&self, samples: &[String]) {
    if self.conf.is_none() {
      return;
    }
    let tiff_path = self.gen_train_tiff_file_name();
    ConvertCmd::new(samples, tiff_path).exec();
  }

  pub fn make_box(&self) {
    if self.conf.is_none() {
      return;
    }
    let tiff_file = self.gen_train_tiff_file_name();
    let box_file = self.gen_train_file_name();
    MakeBoxCmd::new(&tiff_file, &box_file, 440, 3).exec();
  }

  pub fn sync_to_toml(&mut self) {
    if self.conf.is_none() {
      return;
    }
    self.conf.as_mut().unwrap().plus_num(); //merge png to tiff 后num+1

    write_train_conf_to_toml(
      self.dir.parent().unwrap(),
      self.conf.as_ref().unwrap(),
      true,
    )
    .expect("update train conf failed");
  }
}

impl OcrTrainer for TesseractTrainer {
  type Output = Result<(), Error>;
  fn train(&self) -> Self::Output {
    Ok(())
  }
}
